﻿/*!	@file	r_gui_widget_poplist.cpp
	@brief	Window_GUI
Copyright (c) 2010 Yuya Yamaguchi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "r_gui_widget_poplist.h"
#include "r_gui_server.h"
#include "r_gui_draw.h"
#include "r_gui_event.h"
#include <algorithm>

// >>>>>>>>>> RTTI >>>>>>>>>>
#include "../rtti/r_rtti_reflection_implement.h"
namespace r{
	namespace gui{
RRTTI_REFLECTION_IMPLEMENT ( r::gui::WidgetPopList, r::gui::Widget )
// <<<<<<<<<< RTTI <<<<<<<<<<

		const	static	r_si32	sRGUIWidgetPopList_Char_Stride			= 9;		//!< 文字の高さ
		const	static	r_si32	sRGUIWidgetPopList_Menu_Stride			= 10;		//!< 1項目あたりの高さ
		const	static	r_si32	sRGUIWidgetPopList_Separator_Stride		= 6;		//!< セパレータがあった場合の高さ
		const	static	r_si32	sRGUIWidgetPopList_Separator_SizeH		= 2;		//!< セパレータの高さ
		const	static	r_si32	sRGUIWidgetPopList_Separator_OffY		= ( sRGUIWidgetPopList_Separator_Stride - sRGUIWidgetPopList_Separator_SizeH ) >> 1;


		const	static	r_si32	sRGUIWidgetPopList_Submenu_SizeW		= 10;		//!< サブメニューアイコンの幅

		WidgetPopList :: WidgetPopList()
			:
			submenuWidget ( NULL ),
			hoverIndex ( -1 ),
			oldHoverIndex ( -1 ),
			removeNext ( false )
		{
			minWidth	= 20;
			minHeight	= 16;

			posX		= 0;
			posY		= 0;
			posW		= minWidth;
			posH		= minHeight;
			posOffR		= posOffL		= 4;
			posOffT		= 4;
			posOffB		= 4;
		}

		WidgetPopList :: ~WidgetPopList()
		{
		}

		typeConnection	WidgetPopList :: connectSlot ( const typeSignals<void ( r_si32 ) >::slot_type &slot )
		{
			return signalSelect.connect ( slot );
		}

		void	WidgetPopList :: setMenu ( MenuInfo& menuInfo )
		{
			menu			= menuInfo;
			// メニュー解析
			// サイズの割り出し
			r_si32	num		= menu.getSubMenuNum();
			posW			= 0;
			posH			= 0;
			for ( r_si32 i = 0; i < num; ++i ) {
				const	MenuInfo*	submenu	= menu.getSubMenu ( i );
				switch ( submenu->getType() ) {
				case MenuType_Menu:
					posH	+= sRGUIWidgetPopList_Menu_Stride;
					// 大まかな横幅を算出
					posW	= std::max ( submenu->getNameLength() * sRGUIWidgetPopList_Char_Stride + ( submenu->getSubMenuNum() > 0 ? sRGUIWidgetPopList_Submenu_SizeW : 0 ), posW );
					break;
				case MenuType_Separator:
					posH	+= sRGUIWidgetPopList_Separator_Stride;
					break;
				}
			}
			posW	+= posOffR + posOffL;
			posH	+= posOffT + posOffB;
		}

		void	WidgetPopList :: updateCkickInRectLMB ( Event& guiEvent )
		{
			if ( ( getVisible() == eRGUIVisible_Hide ) || ( getVisible() == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBClick() != true ) {
				return;
			}
			if ( guiEvent.getLMBDragWidget() != NULL ) {
				return;
			}
			// 子の階層から調査を行う
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( getVisible() != eRGUIVisible_Close ) {
				r_size	num	= getChildNum();
				for ( r_size i = 0; i < num; ++i ) {
					getChild ( i )->updateCkickInRectLMB ( guiEvent );
					if ( guiEvent.getLMBDragWidget() != NULL ) {
						return;
					}
				}
			}
			if ( checkInRect ( x, y ) == true ) {
				onClickInRectLMB ( guiEvent );
			}
		}

		void	WidgetPopList :: updateReleaseInRectLMB ( Event& guiEvent )
		{
			if ( ( getVisible() == eRGUIVisible_Hide ) || ( getVisible() == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( guiEvent.getLMBRelease() != true ) {
				return;
			}
			// 直接登録インスタンスを呼び出さずに現在のGUIツリーを辿ることで削除済みデータへのアクセスガード
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			if ( getVisible() != eRGUIVisible_Close ) {
				r_size	num	= getChildNum();
				for ( r_size i = 0; i < num; ++i ) {
					getChild ( i )->updateReleaseInRectLMB ( guiEvent );
				}
			}
			if ( ( guiEvent.getLMBOldDragWidget() == this ) && ( checkInRect ( x, y ) == true ) ) {
				onReleaseInRectLMB ( guiEvent );
			}
		}

		void	WidgetPopList :: updateHoverInRect ( Event& guiEvent )
		{
			if ( ( getVisible() == eRGUIVisible_Hide ) || ( getVisible() == eRGUIVisible_Ghost ) ) {
				return;
			}
			if ( ( guiEvent.getLMBHover() != true ) || ( guiEvent.getLMBRelease() == true ) ) {
				return;
			}
			if ( ( guiEvent.getRMBHover() != true ) || ( guiEvent.getRMBRelease() == true ) ) {
				return;
			}
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			r_size	num	= getChildNum();
			for ( r_size i = 0; i < num; ++i ) {
				getChild ( i )->updateHoverInRect ( guiEvent );
			}
			if ( checkInRect ( x, y ) == true ) {
				isHover			= true;
				onHoverInRect ( guiEvent );
			}
		}

		void	WidgetPopList :: onUpdate ( Event& guiEvent )
		{
			if ( removeNext == true ) {
				Server::instance	gui;
				gui->remove ( this );
			}
		}

		void	WidgetPopList :: onDraw ( draw::Draw& guidraw )
		{
			// 自分の領域にあわせて描画領域を作成
			guidraw.setZ ( guidraw.getZ() + 1 );
			guidraw.setRect ( wposX, wposY, posW, posH );
			guidraw.asignRect();

			guidraw.asignGUITexture();

			Server::instance		gui;
			const DrawInfo&		guiinfo = gui->getDrawInfo();
			guidraw.drawWindow ( wposX, wposY, posW, posH, draw::WIndows_FlatBox, guiinfo.activeWindowColor );

			// まずはセパレータとサブメニュー用の矢印
			r_si32	num		= menu.getSubMenuNum();
			r_si32	tmpY	= 0;
			for ( r_si32 i = 0; i < num; ++i ) {
				const	MenuInfo*	submenu	= menu.getSubMenu ( i );
				if ( submenu->getType() == MenuType_Separator ) {
					draw::Rect	trect;
					trect.x					= wposX + posOffR;
					trect.y					= wposY + posOffT + tmpY + sRGUIWidgetPopList_Separator_OffY;
					trect.width				= posW - ( posOffR + posOffL );
					trect.height			= sRGUIWidgetPopList_Separator_SizeH;
					trect.uvX	= trect.uvY	= 0;
					trect.uvWidth	=	trect.uvHeight	= 0;
					guidraw.drarwRect ( trect, guiinfo.activeCharColor );
					tmpY	+= sRGUIWidgetPopList_Separator_Stride;
				} else {
					if ( submenu->getSubMenuNum() > 0 ) {
						guidraw.drawIcon ( wposX + posW - sRGUIWidgetPopList_Submenu_SizeW - posOffL, wposY + tmpY + posOffT, draw::Icons_Triangle, guiinfo.activePartColor, draw::IconsRotate_270 );
					}
					// ホバー位置にハイライト
					if ( hoverIndex == i ) {
						draw::Rect	trect;
						trect.x					= wposX;
						trect.y					= wposY + posOffT + tmpY;
						trect.width				= posW;
						trect.height			= sRGUIWidgetPopList_Menu_Stride;
						trect.uvX	= trect.uvY	= 0;
						trect.uvWidth	=	trect.uvHeight	= 0;
						guidraw.drarwRect ( trect, guiinfo.hoverWPartColor );
					}
					tmpY	+= sRGUIWidgetPopList_Menu_Stride;
				}
			}
			// 続いて文字列
			tmpY	= 0;
			for ( r_si32 i = 0; i < num; ++i ) {
				const	MenuInfo*	submenu	= menu.getSubMenu ( i );
				if ( submenu->getType() == MenuType_Separator ) {
					tmpY	+= sRGUIWidgetPopList_Separator_Stride;
				} else {
					if ( submenu->getNameLength() > 0 ) {
						// テキストの描画
						guidraw.drawText ( wposX + posOffR, wposY + tmpY + posOffT, submenu->getName(), guiinfo.activeCharColor );
					}
					tmpY	+= sRGUIWidgetPopList_Menu_Stride;
				}
			}
			// 子に向けて描画領域の設定
			guidraw.setZ ( guidraw.getZ() + 1 );
			guidraw.setRect ( wposX + posOffL, wposY + posOffT, posW - ( posOffL + posOffR ), posH - ( posOffT + posOffB ) );
			guidraw.asignRect();
		}

		void	WidgetPopList :: onClickInRectLMB ( Event& guiEvent )
		{
			guiEvent.setLMBDrag ( this );
		}

		void	WidgetPopList :: onDoubleClickInRectLMB ( Event& guiEvent )
		{
		}

		void	WidgetPopList :: onReleaseInRectLMB ( Event& guiEvent )
		{
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			r_si32	tx	=	x - wposX;
			r_si32	ty	=	y - wposY;

			r_si32	num		= menu.getSubMenuNum();
			r_si32	tmpY	= 0;
			hoverIndex		= -1;
			for ( r_si32 i = 0; i < num; ++i ) {
				const	MenuInfo*	submenu	= menu.getSubMenu ( i );
				if ( submenu->getType() == MenuType_Separator ) {
					tmpY	+= sRGUIWidgetPopList_Separator_Stride;
				} else {
					if ( ( tmpY <= ty ) && ( tmpY + sRGUIWidgetPopList_Menu_Stride >= ty ) ) {
						hoverIndex	= i;
						signalSelect ( submenu->getID() );
						break;
					}
					tmpY	+= sRGUIWidgetPopList_Menu_Stride;
				}
			}
			// 自滅
			if ( getParent() == NULL ) {
				removeNext	= true;
			}
		}

		void	WidgetPopList :: onReleaseOutRectLMB ( Event& guiEvent )
		{
			// 自滅
			if ( getParent() == NULL ) {
				removeNext	= true;
			}
		}

		void	WidgetPopList :: onDragLMB ( Event& guiEvent )
		{
			r_si32	b_x;
			r_si32	b_y;
			r_si32	n_x;
			r_si32	n_y;
			guiEvent.getDragPos ( b_x, b_y );
			guiEvent.getCurPos ( n_x, n_y );

			// ホバー位置
		}

		void	WidgetPopList :: onDragHoverInRectLMB ( Event& guiEvent )
		{
		}

		void	WidgetPopList :: onDragReleaseInRectLMB ( Event& guiEvent )
		{
		}

		void	WidgetPopList :: onHoverInRect ( Event& guiEvent )
		{
			r_si32	x;
			r_si32	y;
			guiEvent.getCurPos ( x, y );
			r_si32	tx	=	x - wposX;
			r_si32	ty	=	y - wposY;

			r_si32	num		= menu.getSubMenuNum();
			r_si32	tmpY	= 0;
			oldHoverIndex	= hoverIndex;
			hoverIndex		= -1;
			for ( r_si32 i = 0; i < num; ++i ) {
				const	MenuInfo*	submenu	= menu.getSubMenu ( i );
				if ( submenu->getType() == MenuType_Separator ) {
					tmpY	+= sRGUIWidgetPopList_Separator_Stride;
				} else {
					if ( ( tmpY <= ty ) && ( tmpY + sRGUIWidgetPopList_Menu_Stride >= ty ) ) {
						hoverIndex	= i;
						// 前回とサブメニューが変わったら
						if ( oldHoverIndex != hoverIndex ) {
							if ( submenuWidget != NULL ) {
								Server::instance	gui;
								gui->unParent ( submenuWidget, NULL );
								delete submenuWidget;
								submenuWidget	= NULL;
							}
							if ( submenu->getSubMenuNum() > 0 ) {
								submenuWidget	= new WidgetPopList();
								submenuWidget->connectSlot ( signalsBind ( &WidgetPopList :: onSubMenuCall, this, _1 ) );
								submenuWidget->setMenu ( *menu.getSubMenu ( i ) );
								submenuWidget->setPos ( posW - posOffR, tmpY );
								addChild ( submenuWidget );
							}
						}
						break;
					}
					tmpY	+= sRGUIWidgetPopList_Menu_Stride;
				}
			}
		}

		void	WidgetPopList :: onSubMenuCall ( r_si32 id )
		{
			signalSelect ( id );
			// 自滅準備
			if ( getParent() == NULL ) {
				removeNext	= true;
			}
		}
	}
}

